#include<iostream>
#include<vector>
#include<queue>
#include<unordered_map>
#include<atomic>
#include<chrono>
#include<mutex>
#include<thread>
#include<condition_variable>
#include<functional>

using namespace std;

class TimeManager
{
	using TimePoint = std::chrono::steady_clock::time_point;
	using CallBack = function<void()>;
	using TimerID = uint64_t;

	TimeManager()
		:nextId(1),isRunning(true)
	{
		worker = thread(&TimeManager::Work_Thread, this);
	}
	~TimeManager()
	{
		stop();
	}
public:
	void stop()
	{
		isRunning = false;
		cv.notify_all();
		if (worker.joinable())
			worker.join();
	}

	TimerID AddTimer(uint64_t delay_ms,CallBack callback)
	{
		auto now = chrono::steady_clock::now();
		auto expire = now + chrono::microseconds(delay_ms);
		TimerID id = nextId.fetch_add(1);

		{
			lock_guard<mutex> lock(mtx);
			auto timer = new Timer(id, expire, callback);
			timers.push(*timer);
			timerMap[id] = timer;
		}

		cv.notify_one();
		return id;
	}

	void CancelTimer(TimerID id)
	{
		lock_guard<mutex> lock(mtx);
		if (timerMap.find(id) != timerMap.end()) 
		{
			auto timer = timerMap[id];
			timer->cancelled = true;
			timerMap.erase(id);
		}
	}
private:
	struct Timer
	{
		TimerID id;
		TimePoint expire;
		CallBack callBack;
		bool cancelled = false;

		bool operator> (const Timer& t) const
		{
			return expire > t.expire;
		}

		Timer(TimerID _id,TimePoint _expire,CallBack _callBack)
			:id(_id),expire(_expire),callBack(_callBack)
		{ }
	};

	void Work_Thread()
	{
		while (isRunning)
		{
			unique_lock<mutex> lock(mtx);
			if (timers.empty())
			{
				cv.wait(lock);
				continue;
			}

			Timer timer = timers.top();
			auto now = chrono::steady_clock::now();

			if (timer.expire <= now)
			{
				timers.pop();
				timerMap.erase(timer.id);
				lock.unlock();

				if (!timer.cancelled)
				{
					timer.callBack();
				}
			}
			else
			{
				cv.wait_until(lock, timer.expire);
			}
		}
	}

	priority_queue<Timer, vector<Timer>, greater<Timer>> timers;
	unordered_map<TimerID, Timer*> timerMap;
	atomic<TimerID> nextId;
	mutex mtx;
	condition_variable cv;
	thread worker;
	atomic<bool> isRunning;
};

int main()
{

}